package com.panghl.config;

import com.panghl.filter.JWTAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * @Author panghl
 * @Date 2022/2/12
 * @Description TODO
 **/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启权限注解功能
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    JWTAuthenticationFilter jwtAuthenticationFilter;

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private AccessDeniedHandler accessDeniedHandler;


    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private AuthenticationSuccessHandler successHandler;

    @Autowired
    private AuthenticationFailureHandler failureHandler;

    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;

    @Override
    public void configure(WebSecurity web) throws Exception {
        /*静态资源过滤*/
        web.ignoring().antMatchers("/css/**","/images/**","/js/**","/code/images");
    }

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问 【未登录是可以访问的，已登录是不可访问的】
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问（非remember-me下自动登录）
     * hasAnyAuthority     |   如果有参数，参数表示权限，则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数，参数表示角色，则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数，参数表示权限，则其权限可以访问
     * hasIpAddress        |   如果有参数，参数表示IP地址，如果用户IP和参数匹配，则可以访问
     * hasRole             |   如果有参数，参数表示角色，则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf().disable()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 对于登录接口 允许匿名访问【未登录是可以访问的，已登录是不可访问的】   permitAll 登录与否都可以访问
                .antMatchers("/user/login").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();
        // 添加过滤器 自定义JWT认证过滤器
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        //配置异常处理器
        http.exceptionHandling()
                //配置认证失败处理器
                .authenticationEntryPoint(authenticationEntryPoint)
                //授权失败处理器
                .accessDeniedHandler(accessDeniedHandler);

        //允许跨域
        http.cors();
//        http.formLogin()
////                配置认证成功处理器
//                .successHandler(successHandler)
////                配置认证失败处理器
//                .failureHandler(failureHandler);

        http.logout()
                .logoutUrl("/user/logout")
                //配置注销成功处理器
                .logoutSuccessHandler(logoutSuccessHandler);


        // 表单认证
        /*http.formLogin()
                .loginPage("/toLoginPage")
                .loginProcessingUrl("/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .successForwardUrl("/")

                .and().rememberMe()
                .tokenValiditySeconds(1209600) //token失效时间 默认是2周
                .rememberMeParameter("remember-me") //自定义表单值
                .tokenRepository(getPersistentTokenRepository())

                .and()
                .authorizeRequests()
                // 对于登录接口 允许匿名访问【未登录是可以访问的，已登录是不可访问的】   permitAll 登录与否都可以访问
                .antMatchers("/toLoginPage").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();

        // 加载同源域名下iframe页面
        http.headers().frameOptions().sameOrigin();

        http.sessionManagement() //设置session管理
                .invalidSessionUrl("/toLoginPage") //session失效之后跳转的路径
                .maximumSessions(1)  //session最大会话数 1，同时一时间只能有一个用户可以登录
                .maxSessionsPreventsLogin(true) //如果达到最大会话数量，就阻止登录
                .expiredUrl("toLoginPage"); //session过期之后跳转的路径
                */
    }


    /**
     * 解决 无法直接注入 AuthenticationManager
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }



/*    @Autowired
    DataSource dataSource;
    @Bean
    public PersistentTokenRepository getPersistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource); // 设置数据源
        tokenRepository.setCreateTableOnStartup(true); // 启动时帮助我们自动创建一张表，第一次启动设置true 第二次启动设置false
        return tokenRepository;
    }*/

}
